<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <meta name="author" content="Mouse0w0">
  
  <link rel="shortcut icon" href="../img/favicon.ico">
  <title>雾 - Lwjglbook中文翻译</title>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700" />

  <link rel="stylesheet" href="../css/theme.css" />
  <link rel="stylesheet" href="../css/theme_extra.css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" />
  
  <script>
    // Current page data
    var mkdocs_page_name = "\u96fe";
    var mkdocs_page_input_path = "16-fog.md";
    var mkdocs_page_url = null;
  </script>
  
  <script src="../js/jquery-2.1.1.min.js" defer></script>
  <script src="../js/modernizr-2.8.3.min.js" defer></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
  <script>hljs.initHighlightingOnLoad();</script> 
  
</head>

<body class="wy-body-for-nav" role="document">

  <div class="wy-grid-for-nav">

    
    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
    <div class="wy-side-scroll">
      <div class="wy-side-nav-search">
        <a href=".." class="icon icon-home"> Lwjglbook中文翻译</a>
        <div role="search">
  <form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
    <input type="text" name="q" placeholder="Search docs" title="Type search term here" />
  </form>
</div>
      </div>

      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../01-first-steps/">事前准备</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../02-the-game-loop/">游戏循环</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../03-a-brief-about-coordinates/">坐标简介</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../04-rendering/">渲染</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../05-more-on-rendering/">渲染补充</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../06-transformations/">变换</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../07-textures/">纹理</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../08-camera/">摄像机</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../09-loading-more-complex-models/">加载更复杂的模型</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../10-let-there-be-light/">要有光</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../11-let-there-be-even-more-light/">要有更多的光</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../12-game-hud/">游戏HUD</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../13-sky-box-and-some-optimizations/">天空盒与一些优化</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../14-height-maps/">高度图</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../15-terrain-collisions/">地形碰撞</a>
                    </li>
                </ul>
                <ul class="current">
                    <li class="toctree-l1 current"><a class="reference internal current" href="./">雾</a>
    <ul class="current">
    </ul>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../17-normal-mapping/">法线贴图</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../18-shadows/">阴影</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../19-animations/">动画</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../20-particles/">粒子</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../21-instanced-rendering/">实例化渲染</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../22-audio/">音效</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../23-3d-object-picking/">三维物体选取</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../24-hud-revisited/">回顾HUD - NanoVG</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../25-optimizations-frustum-culling/">优化 - 截锥剔除</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../26-cascaded-shadow-maps/">级联阴影映射</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../27-assimp/">Assimp库</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../28-deferred-shading/">延迟着色法</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../a01-opengl-debugging/">附录 A - OpenGL调试</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../glossary/">术语表</a>
                    </li>
                </ul>
      </div>
    </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">

      
      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
        <a href="..">Lwjglbook中文翻译</a>
      </nav>

      
      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="breadcrumbs navigation">
  <ul class="wy-breadcrumbs">
    <li><a href="..">Docs</a> &raquo;</li>
    
      
    
    <li>雾</li>
    <li class="wy-breadcrumbs-aside">
      
        <a href="https://github.com/Mouse0w0/lwjglbook-CN-Translation/edit/master/docs/16-fog.md"
          class="icon icon-github"> Edit on GitHub</a>
      
    </li>
  </ul>
  
  <hr/>
</div>
          <div role="main">
            <div class="section">
              
                <h1 id="fog">雾（Fog）</h1>
<p>在处理更复杂的问题之前，我们将学习如何在游戏引擎中创建雾特效。有了这个特效，就可以模拟遥远的物体变暗，似乎消失在浓雾中。</p>
<p>让我们来确定定义雾的属性是什么。第一个是雾的颜色。在现实世界中，雾是灰色的，但我们可以利用这个特效来模拟不同颜色的雾覆盖的区域。还有一个属性是雾的浓度。</p>
<p>因此，为了使用雾特效，我们需要找到一种方法，当3D场景的物体远离摄像机时，使它们褪色到雾的颜色。靠近摄像机的物体不会受到雾的影响，但远处的物体将无法分辨。因此，我们需要计算一个参数，可以用来混合雾的颜色与每个片元的颜色，以模拟雾特效。这个参数取决于与摄像机相距的距离。</p>
<p>让我们把这个参数命名为<code>fogFactor</code>，并设定它的范围为0到1。当<code>fogFactor</code>为1时，意味着物体完全不会受到雾的影响，也就是说，它是较近的物体。当<code>fogFactor</code>为0时，意味着物体完全隐藏在雾中。</p>
<p>然后，计算雾色的方程如下：</p>
<p>
<script type="math/tex; mode=display">finalColour = (1 - fogFactor) \cdot fogColour + fogFactor \cdot framentColour</script>
</p>
<ul>
<li><strong>finalColour</strong> 是使用雾特效的最终颜色。</li>
<li><strong>fogFactor</strong> 是控制雾的颜色与片元的颜色如何混合的参数，它基本上控制物体的可见性。</li>
<li><strong>fogColour</strong> 是雾的颜色。</li>
<li><strong>fragmentColour</strong> 没有使用雾特效的片元颜色。</li>
</ul>
<p>现在我们需要找到一种方法来基于距离计算<strong>fogFactor</strong>。我们可以选择不同的模型，首先使用线性模型。这是一个给定距离以线性方式改变<strong>fogFactor</strong>的模型。</p>
<p>线性模型由以下参数定义：</p>
<ul>
<li><strong>fogStart</strong>: 开始使用雾特效的距离。</li>
<li><strong>fogFinish</strong>: 雾特效达到最大值的距离。</li>
<li><strong>distance</strong>: 到摄像机的距离。</li>
</ul>
<p>有了这些参数，方程就会是这样的：</p>
<p>
<script type="math/tex; mode=display">\displaystyle fogFactor = \frac{(fogFinish - distance)}{(fogFinish - fogStart)}</script>
</p>
<p>对于距离低于<strong>fogStart</strong>的物体我们简单地设置<strong>fogFactor</strong>为<strong>1</strong>。下图表明了<strong>fogFactor</strong>是如何随着距离变化而变化的。</p>
<p><img alt="线性模型" src="../_static/16/linear_model.png" /></p>
<p>线性模型易于计算，但不太真实，因为它不考虑雾气浓度。实际上雾往往以更平滑的方式增加。所以下一个合适的模型是指数模型。该模型的方程如下：</p>
<p>
<script type="math/tex; mode=display">\displaystyle focFactor = e^{-(distance \cdot fogDensity)^{exponent}} = \frac{1}{e^{(distance \cdot fogDensity)^{exponent}}}</script>
</p>
<p>其中的新变量如下所述：</p>
<ul>
<li><strong>fogDensity</strong> 是雾的厚度或浓度。</li>
<li><strong>exponent</strong> 用来控制雾随着距离的增加增长的速度。</li>
</ul>
<p>下图显示了两个图形，分别设置了不同的<strong>exponent</strong>值（蓝线为<strong>2</strong>，红线为<strong>4</strong>）。</p>
<p><img alt="指数模型" src="../_static/16/exponential_model.png" /></p>
<p>在代码中，我们将使用一个公式，让它可以为<strong>exponent</strong>设置不同的值（你可以很容易地修改示例以使用不同的值）。</p>
<p>既然已经解释过这个原理了，我们就可以实现它。我们将在场景的片元着色器中实现雾特效，因为这有我们需要的所有变量。我们将首先定义一个储存雾属性的结构体。</p>
<pre><code class="glsl">struct Fog
{
    int active;
    vec3 colour;
    float density;
};
</code></pre>

<p><code>active</code>属性用于激活或关闭雾特效。雾属性将通过另一个被称作<code>fog</code>的Uniform传递给着色器。</p>
<pre><code class="glsl">uniform Fog fog;
</code></pre>

<p>我们还将创建一个包含着雾属性的名为<code>Fog</code>的新类，它是另一个POJO（Plain Ordinary Java Object，简单的Java对象）。</p>
<pre><code class="java">package org.lwjglb.engine.graph.weather;

import org.joml.Vector3f;

public class Fog {

    private boolean active;

    private Vector3f colour;

    private float density;

    public static Fog NOFOG = new Fog();

    public Fog() {
        active = false;
        this.colour = new Vector3f(0, 0, 0);
        this.density = 0;
    }

    public Fog(boolean active, Vector3f colour, float density) {
        this.colour = colour;
        this.density = density;
        this.active = active;
    }

   // Getters and setters here….
</code></pre>

<p>我们将在<code>Scene</code>类中添加一个<code>Fog</code>示例。默认情况下，<code>Scene</code>类将初始化一个<code>Fog</code>示例到常量<code>NOFOG</code>，用于模拟关闭雾特效的情况。</p>
<p>因为添加了一个新的Uniform类型，所以我们需要修改<code>ShaderProgram</code>类来创建和初始化雾的Uniform。</p>
<pre><code class="java">public void createFogUniform(String uniformName) throws Exception {
    createUniform(uniformName + &quot;.active&quot;);
    createUniform(uniformName + &quot;.colour&quot;);
    createUniform(uniformName + &quot;.density&quot;);
}

public void setUniform(String uniformName, Fog fog) {
    setUniform(uniformName + &quot;.activeFog&quot;, fog.isActive() ? 1 : 0);
    setUniform(uniformName + &quot;.colour&quot;, fog.getColour() );
    setUniform(uniformName + &quot;.density&quot;, fog.getDensity());
}
</code></pre>

<p>在<code>Renderer</code>类中，我们只需要在<code>setupSceneShader</code>方法中创建Uniform：</p>
<pre><code class="java">sceneShaderProgram.createFogUniform(&quot;fog&quot;);
</code></pre>

<p>然后在<code>renderScene</code>方法中使用它：</p>
<pre><code class="java">sceneShaderProgram.setUniform(&quot;fog&quot;, scene.getFog());
</code></pre>

<p>我们现在可以在游戏中定义雾特效，但是需要回到片元着色器中应用雾特效。我们将创建一个名为<code>calcFog</code>的函数，函数定义如下。</p>
<pre><code class="glsl">vec4 calcFog(vec3 pos, vec4 colour, Fog fog)
{
    float distance = length(pos);
    float fogFactor = 1.0 / exp( (distance * fog.density)* (distance * fog.density));
    fogFactor = clamp( fogFactor, 0.0, 1.0 );

    vec3 resultColour = mix(fog.colour, colour.xyz, fogFactor);
    return vec4(resultColour.xyz, colour.w);
}
</code></pre>

<p>如你所见，我们首先计算到顶点的距离。顶点坐标定义在<code>pos</code>变量中，我们只需要计算长度。然后利用<strong>exponent</strong>为2的指数模型（相当于乘以两次）计算雾参数。我们得到的<code>fogFactor</code>的范围在<strong>0</strong>到<strong>1</strong>之间，并使用<code>mix</code>函数。在GLSL中，<code>min</code>函数被用于混合雾色和图元颜色（由颜色变量定义）。相当于使用如下方程：</p>
<p>
<script type="math/tex; mode=display">resultColour = (1 - fogFactor) \cdot fog.colour + fogFactor \cdot colour</script>
</p>
<p>我们还为颜色保留了<strong>w</strong>元素，即透明度。我们不希望这个元素受到影响，片元应该保持它的透明程度不变。</p>
<p>在应用了所有的光效之后，在片元着色器的最后，如果雾特效启用的话，我们将简单地把返回值设置为片元颜色。</p>
<pre><code class="glsl">if ( fog.activeFog == 1 ) 
{
    fragColor = calcFog(mvVertexPos, fragColor, fog);
}
</code></pre>

<p>所有这些代码完成后，我们可以用下面的数据设置一个雾特效：</p>
<pre><code class="java">scene.setFog(new Fog(true, new Vector3f(0.5f, 0.5f, 0.5f), 0.15f));
</code></pre>

<p>然后我们将获得像这样的效果：</p>
<p><img alt="雾特效" src="../_static/16/fog_effect.png" /></p>
<p>你会看到远处的物体褪色，当你靠近它们时，雾开始消失。但有一个问题，天空盒看起来有点奇怪，地平线不受雾的影响。有几种方法可以解决这个问题：</p>
<ul>
<li>使用只能看到天空的另一个不同的天空盒。</li>
<li>删除天空盒，因为有浓雾，你不应该能够看到一个背景。</li>
</ul>
<p>也可能这两个解决方案没有适合你的，你可以试着将雾色与天空盒的背景相匹配，但这样你会做复杂的计算，结果也许不会更好。</p>
<p>如果你运行这个示例，你会感到平行光变得暗淡，场景变暗，但雾看起来有问题，因为它不受光的影响，会看到如下图所示的结果。</p>
<p><img alt="发光的雾" src="../_static/16/glowing_fog.png" /></p>
<p>远处的物体变为雾色，这是一个不受光影响的常数。这造成了一个在黑暗中发光的效果（这可能并不好）。我们需要修改计算雾的函数，让其考虑光照。该函数将接收环境光和平行光来调整雾色。</p>
<pre><code class="glsl">vec4 calcFog(vec3 pos, vec4 colour, Fog fog, vec3 ambientLight, DirectionalLight dirLight)
{
    vec3 fogColor = fog.colour * (ambientLight + dirLight.colour * dirLight.intensity);
    float distance = length(pos);
    float fogFactor = 1.0 / exp( (distance * fog.density)* (distance * fog.density));
    fogFactor = clamp( fogFactor, 0.0, 1.0 );

    vec3 resultColour = mix(fogColor, colour.xyz, fogFactor);
    return vec4(resultColour.xyz, 1);
}
</code></pre>

<p>如你所见，平行光我们仅使用了颜色和强度，我们不需要关注它的方向。这样，我们只需要稍微修改函数的调用：</p>
<pre><code class="glsl">if ( fog.active == 1 ) 
{
    fragColor = calcFog(mvVertexPos, fragColor, fog, ambientLight, directionalLight);
}
</code></pre>

<p>在夜晚时，我们会看到这样的效果。</p>
<p><img alt="夜晚的雾" src="../_static/16/fog_at_night.png" /></p>
<p>一个要强调的重要的事情是，我们必须聪明地选择雾色。这是很重要的，当我们没有天空盒，但有固定的颜色背景，应该把雾色设置为背景色。如果你删除了天空盒的代码并重新运行示例代码，你会得到这样的结果。</p>
<p><img alt="黑色背景" src="../_static/16/fog_clear_colour_black.png" /></p>
<p>但如果我们把背景色修改为（0.5, 0.5, 0.5），最终结果看起来就是如下所示。</p>
<p><img alt="雾灰色背景" src="../_static/16/fog_clear_colour_grey.png" /></p>
              
            </div>
          </div>
          <footer>
  
    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
      
        <a href="../17-normal-mapping/" class="btn btn-neutral float-right" title="法线贴图">Next <span class="icon icon-circle-arrow-right"></span></a>
      
      
        <a href="../15-terrain-collisions/" class="btn btn-neutral" title="地形碰撞"><span class="icon icon-circle-arrow-left"></span> Previous</a>
      
    </div>
  

  <hr/>

  <div role="contentinfo">
    <!-- Copyright etc -->
    
      <p>2019, Mouse0w0</p>
    
  </div>

  Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
      
        </div>
      </div>

    </section>

  </div>

  <div class="rst-versions" role="note" aria-label="versions">
    <span class="rst-current-version" data-toggle="rst-current-version">
      
          <a href="https://github.com/Mouse0w0/lwjglbook-CN-Translation/" class="fa fa-github" style="float: left; color: #fcfcfc"> GitHub</a>
      
      
        <span><a href="../15-terrain-collisions/" style="color: #fcfcfc;">&laquo; Previous</a></span>
      
      
        <span style="margin-left: 15px"><a href="../17-normal-mapping/" style="color: #fcfcfc">Next &raquo;</a></span>
      
    </span>
</div>
    <script>var base_url = '..';</script>
    <script src="../js/theme.js" defer></script>
      <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" defer></script>
      <script src="../search/main.js" defer></script>
    <script defer>
        window.onload = function () {
            SphinxRtdTheme.Navigation.enable(true);
        };
    </script>

</body>
</html>
